﻿// VsPkg.cs : Implementation of BLToolkitUtils_VSPackage
//

using System;
using System.CodeDom.Compiler;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices;
using System.ComponentModel.Design;
using AndreySmirnov.BLToolkitUtils.VSPackage.CodeGeneration;
using AndreySmirnov.BLToolkitUtils.VSPackage.Model.Config;
using AndreySmirnov.BLToolkitUtils.VSPackage.Model.SchemaProvider;
using EnvDTE;
using Microsoft.VisualStudio;
using Microsoft.VisualStudio.Shell.Interop;
using Microsoft.VisualStudio.Shell;
using Microsoft.Win32;
using VSXtra.Package;

namespace AndreySmirnov.BLToolkitUtils.VSPackage
{
    /// <summary>
    /// This is the class that implements the package exposed by this assembly.
    ///
    /// The minimum requirement for a class to be considered a valid package for Visual Studio
    /// is to implement the IVsPackage interface and register itself with the shell.
    /// This package uses the helper classes defined inside the Managed Package Framework (MPF)
    /// to do it: it derives from the Package class that provides the implementation of the 
    /// IVsPackage interface and uses the registration attributes defined in the framework to 
    /// register itself and its components with the shell.
    /// </summary>
    // This attribute tells the registration utility (regpkg.exe) that this class needs
    // to be registered as package.
    [PackageRegistration(UseManagedResourcesOnly = true)]
    // A Visual Studio component can be registered under different regitry roots; for instance
    // when you debug your package you want to register it in the experimental hive. This
    // attribute specifies the registry root to use if no one is provided to regpkg.exe with
    // the /root switch.
    [DefaultRegistryRoot("Software\\Microsoft\\VisualStudio\\9.0")]
    // This attribute is used to register the informations needed to show the this package
    // in the Help/About dialog of Visual Studio.
    [InstalledProductRegistration(true, "#110", "#112", CURRENT_VERSION, IconResourceID = 400)]
    // In order be loaded inside Visual Studio in a machine that has not the VS SDK installed, 
    // package needs to have a valid load key (it can be requested at 
    // http://msdn.microsoft.com/vstudio/extend/). This attributes tells the shell that this 
    // package has a load key embedded in its resources.
    [ProvideLoadKey("Standard", CURRENT_VERSION, "BLToolkit Utilities Integration Package", "Andrey Smirnov", 1)]
    // This attribute is needed to let the shell know that this package exposes some menus.
    [ProvideMenuResource(1000, 1)]
    [Guid(GuidList.guidBLToolkitUtils_VSPackagePkgString)]
	[ProvideEditorFactory(typeof(BLTConfigFileEditorFactory), 200, TrustLevel = __VSEDITORTRUSTLEVEL.ETL_AlwaysTrusted)]
	[ProvideEditorExtension(typeof(BLTConfigFileEditorFactory),
		SETTINGS_FILE_EXTENSION,
		32,
		ProjectGuid = "{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}",
		TemplateDir = PROJECT_ITEMS_DIR,
		NameResourceID = 200)]
	[ProvideAutoLoad("{f1536ef8-92ec-443c-9ed7-fdadf150da82}")]
	public sealed class BLToolkitUtilsPackage : PackageBase, IVsInstalledProduct
    {
        public const string CURRENT_VERSION = "0.1a";
        public const string CURRENT_VERSION_FORMAT = "v." + CURRENT_VERSION +
            ", built {0:MMM dd,yyyy HH:mm:ss UTCzzz}";

    	private uint m_solutionEventsCookie;
    	private readonly SolutionEventsListener m_solutionEventsListener;
    	internal IVsSolution m_solution;


    	/// <summary>
        /// Default constructor of the package.
        /// Inside this method you can place any initialization code that does not require 
        /// any Visual Studio service because at this point the package object is created but 
        /// not sited yet inside Visual Studio environment. The place to do all the other 
        /// initialization is the Initialize method.
        /// </summary>
        public BLToolkitUtilsPackage()
        {
            Trace.WriteLine(string.Format(CultureInfo.CurrentCulture, "Entering constructor for: {0}", this.ToString()));
        	
			m_solutionEventsListener = new SolutionEventsListener(this);
        }

    	/// <summary>
    	/// Releases the resources used by the <see cref="T:Microsoft.VisualStudio.Shell.Package"/> object.
    	/// </summary>
    	/// <param name="disposing">true if the object is being disposed, false if it is being finalized.
    	///                 </param>
    	protected override void Dispose(bool disposing)
    	{
			if (disposing)
			{
				if (m_solution != null && m_solutionEventsCookie != 0)
				{
					m_solution.UnadviseSolutionEvents(m_solutionEventsCookie);
				}
			}
    		base.Dispose(disposing);
    	}


    	/// <summary>
    	/// Called to ask the package if the shell can be closed.
    	/// </summary>
    	/// <returns>
    	/// <see cref="F:Microsoft.VisualStudio.VSConstants.S_OK"/> if the method succeeded, otherwise an error code.
    	/// </returns>
    	/// <param name="canClose">[out] Returns true if the shell can be closed, otherwise false.
    	///                 </param>
    	protected override int QueryClose(out bool canClose)
    	{
    		canClose = true;
    		return VSConstants.S_OK;
    	}

    	/////////////////////////////////////////////////////////////////////////////
        // Overriden Package Implementation
        #region Package Members

        /// <summary>
        /// Initialization of the package; this method is called right after the package is sited, so this is the place
        /// where you can put all the initilaization code that rely on services provided by VisualStudio.
        /// </summary>
        protected override void Initialize()
        {
            Trace.WriteLine (string.Format(CultureInfo.CurrentCulture, "Entering Initialize() of: {0}", this.ToString()));
			base.Initialize();

            // Add our command handlers for menu (commands must exist in the .vsct file)
            OleMenuCommandService mcs = GetService(typeof(IMenuCommandService)) as OleMenuCommandService;
            if ( null != mcs )
            {
                // Create the command for the menu item.
                CommandID menuCommandID = new CommandID(GuidList.guidBLToolkitUtils_VSPackageCmdSet, (int)PkgCmdIDList.cmdidMyCommand);
				OleMenuCommand menuItem = new OleMenuCommand(AddDbDefMenuItemCallback, null, AddDbDefMenuItem_BeforeQueryStatusCallback, menuCommandID);
                mcs.AddCommand( menuItem );
				CommandID updateDbDefCommandID = new CommandID(GuidList.guidBLToolkitUtils_VSPackageCmdSet, (int)PkgCmdIDList.cmdidUpdateDbDefinition);
				menuItem = new OleMenuCommand(UpdateDbDefMenuItemCallback, null, UpdateDbDefMenuItem_BeforeQueryStatusCallback, updateDbDefCommandID);
                mcs.AddCommand( menuItem );
            }

        	m_solution = GetService(typeof (SVsSolution)) as IVsSolution;
        	if (m_solution != null)
			{
				var hr = m_solution.AdviseSolutionEvents(m_solutionEventsListener, out m_solutionEventsCookie);
			}

        }
        #endregion

		private void UpdateDbDefMenuItem_BeforeQueryStatusCallback(object sender, EventArgs e)
		{
			var menuItem = (OleMenuCommand)sender;
			var monitorSelection = (IVsMonitorSelection)GetService(typeof(SVsShellMonitorSelection));

			IVsMultiItemSelect ppMIS;
			IntPtr ppHier, ppSC;
			uint pitemid;
			var hr = monitorSelection.GetCurrentSelection(out ppHier, out pitemid, out ppMIS, out ppSC);
			ErrorHandler.ThrowOnFailure(hr);
			Marshal.Release(ppHier);
			if (ppSC != IntPtr.Zero)
			{
				Marshal.Release(ppSC);
			}
			var hierarchy = (IVsHierarchy)Marshal.GetTypedObjectForIUnknown(ppHier, typeof(IVsHierarchy));
            menuItem.Enabled = hierarchy.EnumChildItems(pitemid, string.Empty).Any(x => x.Caption.EndsWith(SETTINGS_FILE_EXTENSION, StringComparison.InvariantCultureIgnoreCase));
		}

		private void AddDbDefMenuItem_BeforeQueryStatusCallback(object sender, EventArgs e)
		{
			var menuItem = (OleMenuCommand)sender;
			var monitorSelection = (IVsMonitorSelection)GetService(typeof(SVsShellMonitorSelection));

			IVsMultiItemSelect ppMIS;
			IntPtr ppHier, ppSC;
			uint pitemid;
			var hr = monitorSelection.GetCurrentSelection(out ppHier, out pitemid, out ppMIS, out ppSC);
			ErrorHandler.ThrowOnFailure(hr);
			Marshal.Release(ppHier);
			if (ppSC != IntPtr.Zero)
			{
				Marshal.Release(ppSC);
			}
			var hierarchy = (IVsHierarchy)Marshal.GetTypedObjectForIUnknown(ppHier, typeof(IVsHierarchy));
            menuItem.Enabled = !hierarchy.EnumChildItems(pitemid, string.Empty).Any(x => x.Caption.EndsWith(SETTINGS_FILE_EXTENSION, StringComparison.InvariantCultureIgnoreCase));
		}

    	public const string SETTINGS_FILE = "bltoolkit.dbdef";
    	public const string SETTINGS_FILE_EXTENSION = ".dbdef";
    	public const string PROJECT_ITEMS_DIR = @"..\..\Templates\ProjectItems";
    	public const string ITEMS_DIR = @"..\..\Templates\Items";

    	private uint m_statusBarCookie;

		private void UpdateStatusBar(bool usingStatusBar, string text, uint soFar, uint total)
		{
			var statusBar = (IVsStatusbar)GetService(typeof(SVsStatusbar));
			statusBar.Progress(ref m_statusBarCookie, usingStatusBar ? 1 : 0, text, soFar, total);
			if (!usingStatusBar)
			{
				m_statusBarCookie = 0;
			}
		}


		private void UpdateDbDefMenuItemCallback(object sender, EventArgs e)
		{
			var monitorSelection = (IVsMonitorSelection)GetService(typeof(SVsShellMonitorSelection));
			IVsMultiItemSelect ppMIS;
			IntPtr ppHier, ppSC;
			uint pitemid;
			var hr = monitorSelection.GetCurrentSelection(out ppHier, out pitemid, out ppMIS, out ppSC);
			ErrorHandler.ThrowOnFailure(hr);
			var hierarchy = (IVsHierarchy)Marshal.GetTypedObjectForIUnknown(ppHier, typeof(IVsHierarchy));
			Marshal.Release(ppHier);
			if (ppSC != IntPtr.Zero)
			{
				Marshal.Release(ppSC);
            }

            var uiShell = (IVsUIShell)GetService(typeof(SVsUIShell));

			var project = GetProjectFromHierarchy(hierarchy, VSConstants.VSITEMID_ROOT);
			var codeGenerator = new TablesCodeGenerator();
			var codeDomProvider = CodeDomProvider.CreateProvider("CSharp");
			var rootNamespace = (string)project.Properties.Item("RootNamespace").Value;
            var baseProjectPath = (string)project.Properties.Item("LocalPath").Value;
		    //PrintPropertyNames(project);
			foreach (var itemDescriptor in hierarchy.EnumChildItems(pitemid, string.Empty).Where(x => x.Caption.EndsWith(SETTINGS_FILE_EXTENSION)))
			{
				var projectItem = GetProjectItemFromHierarchy(hierarchy, itemDescriptor.ItemId);
			    //PrintPropertyNames(projectItem);
                
                var filePath = (string)projectItem.Properties.Item("LocalPath").Value;
			    var relativePath = Path.GetDirectoryName(filePath) + "\\";
			    relativePath = relativePath.Replace(baseProjectPath, string.Empty);

			    var itemRootNamespace = rootNamespace;

                if (relativePath != string.Empty)
                {
                    //config file is in subfolder, so we need to set namespace correctly
                    var subfolders = relativePath.Split(new[] {"\\"}, StringSplitOptions.RemoveEmptyEntries);
                    itemRootNamespace = subfolders.Aggregate(rootNamespace,
                                                         (current, subfolder) =>
                                                         current + string.Format(".{0}",
                                                                       subfolder.CreateValidIdentifier(codeDomProvider)));
                }
			    Console.WriteLine("Deleting existing code...");
				foreach (var item in projectItem.ProjectItems.Cast<ProjectItem>())
				{
					Console.WriteLine("Deleting {0}...", item.Name);
					item.Delete();
				}
				Console.WriteLine("Deleting existing code is completed.");
				var configName = string.Empty;
				hr = hierarchy.GetCanonicalName(itemDescriptor.ItemId, out configName);
				ErrorHandler.ThrowOnFailure(hr);
				var schemaProvider = SchemaProviderFactory.CreateProvider();
				var configFile = new BLTConfigFile();
                try
                {
                    configFile.Load(configName);
                }
                catch(Exception ex)
                {
                    DisplayExceptionMessage(uiShell, "Update Error",
                        "Unable to load configuration file",
                        ex);
                    return;
                }
                try
                {
                    schemaProvider.Connect(configFile.Config.ConnectionInfo);
                }
                catch(Exception ex)
                {
                    DisplayExceptionMessage(uiShell, "Update Error",
                        "Unable to connect to the server",
                        ex);
                    return;
                }
				var tables = schemaProvider.EnumTables();
				var message = string.Format("Generating code for database {0}...", configFile.Config.ConnectionInfo.DatabaseName);
				UpdateStatusBar(true, message, 0, (uint) tables.Count);
				var count = 0u;
				foreach (var table in tables)
				{
                    try
                    {
						Console.Write("Generating code for table {0}...", table.GetFullTableName());
                        var fields = schemaProvider.GetTableFields(table);
                        var foreignKeys = schemaProvider.GetTableForeignKeys(table);
                        var ccu = codeGenerator.GenerateFileForTable(table, fields, foreignKeys,
                            itemRootNamespace, configFile.Config.GenerationTask.GlobalSettings,
                            (tableName, schema) => tables.FirstOrDefault(x => x.TableName == tableName && x.SchemaName == schema),
                            codeDomProvider);
                        var path = AddDependantFile(hierarchy, 
                            itemDescriptor.ItemId, 
                            table.CreateValidIdentifier(codeDomProvider) + ".cs");
                        using (var writer = new StreamWriter(path, false))
                        {
                            var opts = new CodeGeneratorOptions();
                            opts.ElseOnClosing = false;
                            opts.IndentString = "\t";
                            opts.BlankLinesBetweenMembers = true;
                            opts.BracingStyle = "C";
                            opts.VerbatimOrder = true;
                            codeDomProvider.GenerateCodeFromCompileUnit(ccu, writer, opts);
                        }
						Console.WriteLine("done.");
                    }
                    catch(Exception ex)
                    {
                        DisplayExceptionMessage(uiShell, "Update Error", string.Format("Unable to generate/update code for table '{0}'", table.GetFullTableName()), ex);
					}
					count++;
					UpdateStatusBar(true, message, count, (uint)tables.Count);
				}
				string contextName;
                var contextUnit = new DbContextCodeGeneration().GenerateDbContext(itemRootNamespace,
				                                                                  Path.GetFileNameWithoutExtension(itemDescriptor.Caption),
				                                                                  tables,
				                                                                  codeDomProvider,
                                                                                  configFile.Config,
																				  out contextName);
				var contextFileName = AddDependantFile(hierarchy, itemDescriptor.ItemId, contextName + ".cs");
				using (var writer = new StreamWriter(contextFileName, false))
				{
					var opts = new CodeGeneratorOptions();
					opts.ElseOnClosing = false;
					opts.IndentString = "\t";
					opts.BlankLinesBetweenMembers = true;
					opts.BracingStyle = "C";
					opts.VerbatimOrder = true;
					codeDomProvider.GenerateCodeFromCompileUnit(contextUnit, writer, opts);
				}
            }
		    project.Save(string.Empty);
			UpdateStatusBar(false, string.Empty, 0, 0);
            var clsid = Guid.Empty;
            int result;
			ErrorHandler.ThrowOnFailure(uiShell.ShowMessageBox(
					   0,
					   ref clsid,
					   "BLToolkit Utilities Integration Package",
					   "Update complete!",
					   string.Empty,
					   0,
					   OLEMSGBUTTON.OLEMSGBUTTON_OK,
					   OLEMSGDEFBUTTON.OLEMSGDEFBUTTON_FIRST,
					   OLEMSGICON.OLEMSGICON_INFO,
					   0,        // false
					   out result));
		}

        private static void PrintPropertyNames(ProjectItem projectItem)
        {
            foreach (Property property in projectItem.Properties)
            {
                Console.WriteLine(property.Name);
            }
        }

        private static void PrintPropertyNames(Project projectItem)
        {
            foreach (Property property in projectItem.Properties)
            {
                Console.WriteLine(property.Name);
            }
        }

        private static void DisplayErrorMessage(IVsUIShell uiShell, string title, string message)
        {
            var clsid = Guid.Empty;
            int result;
            ErrorHandler.ThrowOnFailure(uiShell.ShowMessageBox(
                       0,
                       ref clsid,
                       title,
                       message,
                       string.Empty,
                       0,
                       OLEMSGBUTTON.OLEMSGBUTTON_OK,
                       OLEMSGDEFBUTTON.OLEMSGDEFBUTTON_FIRST,
                       OLEMSGICON.OLEMSGICON_CRITICAL,
                       0,        // false
                       out result));
        }

        private static void DisplayExceptionMessage(IVsUIShell uiShell, string title, string errorText, Exception exception)
        {
            var clsid = Guid.Empty;
            int result;
            ErrorHandler.ThrowOnFailure(uiShell.ShowMessageBox(
                       0,
                       ref clsid,
                       title,
                       string.Format("{0}\r\n{1}", errorText, exception.ToString()),
                       string.Empty,
                       0,
                       OLEMSGBUTTON.OLEMSGBUTTON_OK,
                       OLEMSGDEFBUTTON.OLEMSGDEFBUTTON_FIRST,
                       OLEMSGICON.OLEMSGICON_CRITICAL,
                       0,        // false
                       out result));
        }

		private static string AddDependantFile(IVsHierarchy pHier, uint dwItemId, string fileName)
		{
			var projectItem = GetProjectItemFromHierarchy(pHier, dwItemId);
			if (projectItem == null)
				return null;
			string configName;
			var hr = pHier.GetCanonicalName(dwItemId, out configName);
			ErrorHandler.ThrowOnFailure(hr);
			var newFileName = Path.Combine(Path.GetDirectoryName(configName), fileName);
			if (projectItem.ProjectItems.Cast<ProjectItem>().Any(x => x.Name == fileName))
			{
				return newFileName;
			}
			var templateFileName = Path.Combine(GetItemsDir(), "emptyfile.cs");
			var newTemplateFileName = Path.Combine(Path.GetDirectoryName(configName), "emptyfile.cs");
			if (File.Exists(newTemplateFileName))
			{
				File.Delete(newTemplateFileName);
			}
			if (File.Exists(newFileName))
			{
				File.Delete(newFileName);
			}
			var item = projectItem.ProjectItems.AddFromFileCopy(templateFileName);
			item.Name = fileName;
			return newFileName;
		}

		private static ProjectItem GetProjectItemFromHierarchy(IVsHierarchy pHier, uint dwItemId)
		{

			object oObj;
			ErrorHandler.ThrowOnFailure(pHier.GetProperty(dwItemId, (int)__VSHPROPID.VSHPROPID_ExtObject, out oObj));
            var oItem = oObj as ProjectItem;
            if (oItem == null)
			{
                Debug.Print("ArgumentException: Invalid ProjectItem");
                throw new ArgumentException("Invalid ProjectItem");
			}
			return oItem;

		} // func GetProjectItemFromHierarchy

		private EnvDTE.Project GetProjectFromHierarchy(IVsHierarchy pHier, uint dwItemId)
		{

			object oObj;

			ErrorHandler.ThrowOnFailure(pHier.GetProperty(dwItemId, (int)__VSHPROPID.VSHPROPID_ExtObject, out oObj));

			var oItem = oObj as EnvDTE.Project;

			if (oItem == null)
			{

				Debug.Print("ArgumentException: Invalid ProjectItem");

				throw new ArgumentException("Invalid ProjectItem");

			}

			return oItem;

		} // func GetProjectItemFromHierarchy


		private static string GetLibraryDir()
		{
			var dir = Path.GetDirectoryName(typeof (BLToolkitUtilsPackage).Assembly.Location);
			return dir;
		}


        private const string CONFIG_REG_PATH = @"Software\BLToolkitUtils.VSPackage";
        private static string m_projectItemsPath;
        private static string m_ItemsPath;

        private static void InitConfigStrings()
        {
            if (m_projectItemsPath != null)
                return;
            m_projectItemsPath = Path.Combine(GetLibraryDir(), PROJECT_ITEMS_DIR);
            m_ItemsPath = Path.Combine(GetLibraryDir(), ITEMS_DIR);
            if (!Directory.Exists(m_projectItemsPath))
            {
                var reg = Registry.LocalMachine.OpenSubKey(CONFIG_REG_PATH);
                if (reg != null)
                {
                    m_projectItemsPath = reg.GetValue("ProjectItemTemplatesPath") as string;
                    m_ItemsPath = reg.GetValue("ItemsPath") as string;
                }
            }
        }

		private static string GetProjectItemsDir()
		{
		    InitConfigStrings();
			return m_projectItemsPath;
		}

		private static string GetItemsDir()
		{
		    InitConfigStrings();
			return m_ItemsPath;
		}

        /// <summary>
        /// This function is the callback used to execute a command when the a menu item is clicked.
        /// See the Initialize method to see how the menu item is associated to this function using
        /// the OleMenuCommandService service and the MenuCommand class.
        /// </summary>
        private void AddDbDefMenuItemCallback(object sender, EventArgs e)
        {
            var monitorSelection = (IVsMonitorSelection) GetService(typeof (SVsShellMonitorSelection));
        	IVsMultiItemSelect ppMIS;
        	IntPtr ppHier, ppSC;
        	uint pitemid;
        	var hr = monitorSelection.GetCurrentSelection(out ppHier, out pitemid, out ppMIS, out ppSC);
			ErrorHandler.ThrowOnFailure(hr);
			var hierarchy = (IVsHierarchy)Marshal.GetTypedObjectForIUnknown(ppHier, typeof(IVsHierarchy));
        	Marshal.Release(ppHier);
			if (ppSC != IntPtr.Zero)
			{
				Marshal.Release(ppSC);
			}

        	var definitionItem = hierarchy.EnumChildItems(pitemid)
				.FirstOrDefault(x => x.Caption.ToLower() == SETTINGS_FILE);
			if (definitionItem == null)
			{
				//we're about to create db definition
				var project = hierarchy as IVsProject3;

				var fileName = Path.Combine(GetProjectItemsDir(), SETTINGS_FILE);
				var editorId = GuidList.guidBLTConfigEditorFactory;
				var viewId = VSConstants.LOGVIEWID_Primary;
				var results = new VSADDRESULT[1];
				var files = new string[1];
				files[0] = fileName;
				hr = project.AddItemWithSpecific(pitemid,
												 VSADDITEMOPERATION.VSADDITEMOP_CLONEFILE,
												 SETTINGS_FILE,
				                                 1,
												 files,
				                                 IntPtr.Zero,
												 (uint)(__VSSPECIFICEDITORFLAGS.VSSPECIFICEDITOR_DoOpen),
				                                 ref editorId,
				                                 null,
				                                 ref viewId,
				                                 results);
				ErrorHandler.ThrowOnFailure(hr);
			}
        }

        public string GetResourceString(string resourceName)
        {
            string resourceValue;
            IVsResourceManager resourceManager =
                (IVsResourceManager)GetService(typeof(SVsResourceManager));
            if (resourceManager == null)
            {
                throw new InvalidOperationException(
                    "Could not get SVsResourceManager service. Make sure that the package is sited before calling this method");
            }
            Guid packageGuid = GetType().GUID;
            int hr = resourceManager.LoadResourceString(
                ref packageGuid, -1, resourceName, out resourceValue);
            ErrorHandler.ThrowOnFailure(hr);
            return resourceValue;
        }

        #region Branding (IVsInstalledProduct implementation)

        public int IdBmpSplash(out uint pIdBmp)
        {
            pIdBmp = 300;
            return VSConstants.S_OK;
        }

        public int OfficialName(out string pbstrName)
        {
            pbstrName = GetResourceString("@110");
            return VSConstants.S_OK;
        }
        internal static string GetVersionStringInternal()
        {
            var name = typeof(BLToolkitUtilsPackage).Assembly.GetName();
            var buildDate = new DateTime(2000, 1, 1);
            buildDate = buildDate.AddDays(name.Version.Build).AddSeconds(name.Version.Revision * 2);
            if (TimeZone.IsDaylightSavingTime(buildDate, TimeZone.CurrentTimeZone.GetDaylightChanges(buildDate.Year)))
            {
                buildDate = buildDate.AddHours(1);
            }

            return string.Format(CURRENT_VERSION_FORMAT, buildDate);
        }

        public int ProductID(out string pbstrPID)
        {
            pbstrPID = GetVersionStringInternal();
            return VSConstants.S_OK;
        }

        public int ProductDetails(out string pbstrProductDetails)
        {
            pbstrProductDetails = GetResourceString("@112");
            return VSConstants.S_OK;
        }

        public int IdIcoLogoForAboutbox(out uint pIdIco)
        {
            pIdIco = 400;
            return VSConstants.S_OK;
        }

        #endregion

    }
}